﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MapWinGIS;
using System.IO;

namespace MapWinGisTest
{
    /// <summary>
    /// 地图项,结构体对象不能为空。结构为值类型
    /// </summary>
    public struct MapItem
    {
        public string Name { get; set; }
        public string File { get; set; }
        /// <summary>
        /// 图层句柄属性，该属性只能通过3种方式获取，通过axmap类的AddLayer和get_LayerHandle (int LayerPosition)方法，
        /// 通过shapefile类的FileHandle属性获取
        /// </summary>
        public int Handle { get; set; }
        public Shapefile shape { get; set; }
    }
    public partial class MapForm : Form
    {

                              

        /// <summary>
        /// 县级行政区图层
        /// </summary> 
        private string baseMap;

        /// <summary>
        /// 县级字段 
        /// </summary> 
        private string baseField;

        /// <summary>
        /// 省行政区图层
        /// </summary>       
        private string provinceLayer;

        /// <summary>
        /// 省级字段
        /// </summary> 
        private string provinceField;

        /// <summary>
        /// 地级市行政区图层
        /// </summary> 
        private string cityLayer;

        /// <summary>
        /// 地级市字段
        /// </summary> 
        private string cityField;

        /// <summary>
        /// 上一个地图放大级别,用于在地图范围变更事件中判断地图是否被放大或者缩小了。
        /// </summary> 
        private int PreZoomLevel;

        /// <summary>
        /// 最小缩放级别
        /// </summary> 
        private int ZoomMinLimit;

        /// <summary>
        /// 管理地图项的列表，MapWinGis的axmap类中获取图层对象不是非常方便，所以定义一个列表用于管理
        /// </summary> 
        private List<MapItem> MapList;

        /// <summary>
        /// 选择区域
        /// </summary> 
        private Extents SelectionExtent;

        public MapForm()
        {            
            InitializeComponent();
            //初始化一些设置
            ZoomMinLimit = 2;
            baseMap = "中国县界.shp";
            baseField = "NAME";
            provinceLayer = "省级行政区.shp";
            provinceField = "NAME";
            cityLayer = "中国地州界.shp";
            cityField = "NAME";
        }


        /// <summary>
        /// 判断地图是否改变了放大级别,并更新上一个放大级别。并在此限制地图最小的缩放级别
        /// </summary>
        /// <param name="PreLevel">上一个缩放级别</param>
        /// <param name="limit">缩小极限</param>
        /// <param name="Map">地图对象</param>
        /// <returns></returns>
        private bool ZoomLevelIsChanged(ref int PreLevel,int limit,AxMapWinGIS.AxMap Map)
        {
            if (Map.CurrentZoom < limit)
            {
                PreLevel = Map.ZoomToPrev();
                return false;
            }
           else if (PreLevel == Map.CurrentZoom)
                return false;
            else { PreLevel = Map.CurrentZoom;return true; }
        }
        
        /// <summary>
        /// 判断当前图层是否在顶层
        /// </summary>
        /// <param name="LayerHandle"></param>
        /// <param name="Map"></param>
        /// <returns></returns>
        private bool LayerIsTop(int LayerHandle,AxMapWinGIS.AxMap Map)
        {
            return Map.get_LayerPosition(LayerHandle) == Map.NumLayers - 1;
        }

        /// <summary>
        /// 地图范围变更事件，在这里更具地图缩放级别添加或者删除一些标签
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ExtentsChangedHandle(object sender, EventArgs e)
        {
            AxMapWinGIS.AxMap map = sender as AxMapWinGIS.AxMap;
            
            if (map!=null&&ZoomLevelIsChanged(ref PreZoomLevel, ZoomMinLimit, map))
            {
                //在任务栏显示当前缩放级别
                StripStatusLabZoomLevel.Text = string.Format("当前缩放级别为：{0}  ",map.CurrentZoom);
                //缩放级别小于8，隐藏所有图层的标签
                if (map.CurrentZoom < 4)
                {                   
                    foreach(var item in MapList)
                    {                        
                        map.get_LayerLabels(item.Handle).Visible=false;//隐藏标签
                        //如果图层为省级行政区并且还不是顶层，设置图层为顶层
                        if (item.Name == provinceLayer && !LayerIsTop(item.Handle, map))
                        {
                            map.MoveLayerTop(map.get_LayerPosition(item.Handle));
                            MapListCB.SelectedItem = item;
                        }
                    }
                }
                //显示或者添加省级行政区标签 
                if (map.CurrentZoom>=4&& map.CurrentZoom <= 6)
                {                    
                    
                    MapItem mItem = MapList.Find(item => item.Name == provinceLayer);
                    //隐藏其他图层的标签
                    foreach (var lay in MapList)
                    {
                        if (lay.shape != mItem.shape)
                        {
                            map.get_LayerLabels(lay.Handle).Visible = false;//隐藏标签
                        }
                    }
                    if (mItem.shape != null)
                    {
                        if (!LayerIsTop(mItem.Handle, map))
                        {
                            map.MoveLayerTop(map.get_LayerPosition(mItem.Handle));
                            MapListCB.SelectedItem = mItem;
                        }

                        if (mItem.shape.Labels.Count>0)
                        {
                            mItem.shape.Labels.Visible = true;
                        }
                        else
                        {
                            AddLable(mItem.shape, provinceField);
                        }
                    }
                }

                //显示或者添加地级市行政区标签 
                if (map.CurrentZoom >= 7 && map.CurrentZoom <= 8)
                {

                    MapItem mItem = MapList.Find(item => item.Name == cityLayer);
                    //隐藏其他图层的标签
                    foreach(var lay in MapList)
                    {
                        if (lay.shape != mItem.shape)
                        {
                            map.get_LayerLabels(lay.Handle).Visible = false;//隐藏标签
                        }
                    }
                    if (mItem.shape != null)
                    {
                        if (!LayerIsTop(mItem.Handle, map))
                        {
                            map.MoveLayerTop(map.get_LayerPosition(mItem.Handle));
                            MapListCB.SelectedItem = mItem;
                        }

                        if (mItem.shape.Labels.Count > 0)
                        {
                            mItem.shape.Labels.Visible = true;
                        }
                        else
                        {
                            AddLable(mItem.shape, cityField);
                        }
                    }
                }

                //显示或者添加县级行政区标签 
                if (map.CurrentZoom >= 9 && map.CurrentZoom <= 11)
                {

                    MapItem mItem = MapList.Find(item => item.Name == baseMap);
                    //隐藏其他图层的标签
                    foreach (var lay in MapList)
                    {
                        if (lay.shape != mItem.shape)
                        {
                            map.get_LayerLabels(lay.Handle).Visible = false;//隐藏标签
                        }
                    }
                    if (mItem.shape != null)
                    {
                        if (!LayerIsTop(mItem.Handle, map))
                        {
                            map.MoveLayerTop(map.get_LayerPosition(mItem.Handle));
                            MapListCB.SelectedItem = mItem;
                        }

                        if (mItem.shape.Labels.Count > 0)
                        {
                            mItem.shape.Labels.Visible = true;
                        }
                        else
                        {
                            AddLable(mItem.shape, baseField);
                        }
                    }
                }
                //在不同图层显示选择区域,在不同图层显示显示选择区域的效果不是很好，会出现选择省份的部分市县没有选中，故只在一个图层上显示选中区域
                //int Top = map.NumLayers - 1;
                //ShapeSelect(map.get_Shapefile(map.get_LayerHandle(Top)), SelectionExtent,map);
            }

        }

        /// <summary>
        /// 搜索并加载当前程序的map目录下的shape地图文件，如果获取到地图文件则添加到地图选择列表中。
        /// </summary>
        /// <param name="Path"></param>
        /// <param name="Map"></param>
        /// <returns></returns>
        private List<MapItem> LoadMapFiles(string Path,ref AxMapWinGIS.AxMap Map)
        {            
            try
            {
                string[] MapFiles = Directory.GetFiles(Path, "*.shp", SearchOption.TopDirectoryOnly);
                List<MapItem> mapList = new List<MapItem>();
                if (MapFiles.Length > 0)
                {
                    
                    foreach (var MapFile in MapFiles)
                    {
                       Shapefile shapeMap = new Shapefile();
                        if (shapeMap.Open(MapFile, null))
                        {
                            //判断shape文件是否有投影位置信息，如果没有则不将该地图图层添加到地图上。避免影响其他地图显示。
                            if (shapeMap.GeoProjection.IsEmpty)
                            {
                                shapeMap.Close();
                                continue;
                            }
                            MapItem mapItem = new MapItem
                            {
                                Name = MapFile.Substring(MapFile.LastIndexOf("\\") + 1),
                                File = MapFile,
                                Handle = Map.AddLayer(shapeMap,true),
                                shape = shapeMap
                            };
                            mapList.Add(mapItem);
                            if (mapItem.Name == provinceLayer)
                            {
                                AddLable(shapeMap, provinceField);
                                shapeMap.Labels.Visible = false;
                            }
                            if (mapItem.Name == cityLayer)
                            {
                                AddLable(shapeMap, cityField);
                                shapeMap.Labels.Visible = false;
                            }
                            if (mapItem.Name == baseMap)
                            {
                                AddLable(shapeMap, baseField);
                                shapeMap.Labels.Visible = false;
                            }

                        }
                        else { MessageBox.Show("打开shp文件出错:" + shapeMap.get_ErrorMsg(shapeMap.LastErrorCode), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); }
                    }                   
                }
                return mapList;
            }
            catch(Exception e)
            {
                MessageBox.Show("获取地图文件出错，错误信息：" + e.Message);
                return null;
            }

        }


        /// <summary>
        /// 将地图文件列表绑定到下拉列表中，注意必须在下拉列表初始化之后才能绑定，否则抛出异常，因为无法操作还不存在的对象
        /// </summary>
        /// <param name="List"></param>
        private void BindingMapList(List<MapItem> List)
        {
            if (List.Count > 0)
            {
                MapListCB.DataSource = List;
                MapListCB.DisplayMember = "Name";
                MapListCB.ValueMember = "File";
            }
        }       

  

        private void AddLable(AxMapWinGIS.AxMap Map,int LayerHandle,string fieldName)
        {
            Shapefile layer = Map.get_Shapefile(LayerHandle);//按照示例，通过图层句柄获取shapefile。虽然我们已经管理了shapefile的引用


        }

        /// <summary>
        /// 添加标签到图层
        /// </summary>
        /// <param name="Layer"></param>
        /// <param name="fieldName"></param>
        private void AddLable(Shapefile Layer, string fieldName)
        {
            if (Layer != null)
            {
                //设置标签文字大小，字体，颜色
                Layer.Labels.FontName = "微软雅黑";
                Layer.Labels.FontSize = 12;
                Layer.Labels.FontBold = false;
                Layer.Labels.FontColor =0;//用正整数表示颜色，使用位运算符号进行操作，将255以二进制向左移动一个字节
                // 1111 1111 -> 1111 1111 0000 0000 
                //这里R为最后一个字节，B为倒数第三个字节,G为倒数第二个字节，颜色顺序刚好是BGR

                //设置文字轮廓大小颜色
                Layer.Labels.FontOutlineVisible = false;
                //Layer.Labels.FontOutlineColor= (255 << 16) + (255 << 8) + 255; //white 每字节代表一个颜色，RGB一共三个字节，B向前移动两个字节，G向前移动一个字节
                //Layer.Labels.FontOutlineWidth = 4;

                //设置文字环绕
                Layer.Labels.HaloVisible = false;

                //设置外框
                Layer.Labels.FrameVisible = false;

                Layer.Labels.Generate("[" + fieldName + "]", tkLabelPositioning.lpCentroid, true);
            }            

        }



        private void MapForm_Load(object sender, EventArgs e)
        {
            //虽然说接口是不能进行实例化的，但是这里有一个例外，就是如果一个接口是com接口，则可以进行实例化。我们可以查看shapefile接口的定义，发现他有一个coclass性特
            //设置坐标单位经纬度或者xy值
            //axMap1.ShowCoordinates = tkCoordinatesDisplay.cdmMapUnits;
            //显示控件版本
            //axMap1.ShowVersionNumber = true;
            //显示重绘时间
            //axMap1.ShowRedrawTime = true;
            //axMap1.GrabProjectionFromData = true;   // default value 从数据自动获取坐标系，默认为true
            
            
            string CurrentDir = Environment.CurrentDirectory;
            MapList=LoadMapFiles(CurrentDir + "\\map",ref aMap);
            BindingMapList(MapList);
            
            if (MapList.Count>0)
            {
                //设置地图列表的选中项为当前的顶层地图
                int TopPosition = aMap.NumLayers-1;//底层为0，顶层为图层总数-1                
                MapListCB.SelectedItem = MapList.Find(item => item.Handle == (aMap.get_LayerHandle(TopPosition)));//MapList的Item实际是之前定义的MapItem类型
                //也可以使用combobox的FindStringExact(String)方法来获取匹配项的索引


               
                //在状态栏设置坐标系和投影坐标系
                StripStatusLabCoordinate.Text = string.Format("坐标系：{0}  ", aMap.GeoProjection.GeogCSName);
                StripStatusLabProjected.Text= string.Format("投影坐标系：{0}  ", aMap.GeoProjection.ProjectionName);
                PreZoomLevel = aMap.CurrentZoom;
                //注册地图范围变更事件
                aMap.SendMouseDown = true;
                aMap.ExtentsChanged += ExtentsChangedHandle;

                //注册选择框完成事件
                aMap.SendSelectBoxFinal = true;
                aMap.SelectBoxFinal += AxMap1SelectBoxFinal; // change MapEvents to axMap1

            }
            
        }


        /// <summary>
        /// 地图列表选择事件，选择将选择的地图图层设置为顶层
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MapListCB_SelectionChangeCommitted(object sender, EventArgs e)
        {
            if (MapListCB.SelectedIndex != -1)
            {
                //string SelectedLayer = MapListCB.GetItemText(MapListCB.SelectedItem);
                //int LayerHandle = MapList.Find(item => item.Name == SelectedLayer).Handle;
                int LayerHandle = ((MapItem)MapListCB.SelectedItem).Handle;
                if (LayerHandle != -1)
                {
                    aMap.MoveLayerTop(aMap.get_LayerPosition(LayerHandle));
                    //int TopPos = axMap1.get_LayerPosition(LayerHandle);
                    //int oPos = axMap1.get_LayerPosition(0);//底层的位置应该为0，顶层地图=图层总数量-1，-1表示找不到该图层
                }
            }
        }

        /// <summary>
        /// 鼠标按键按下事件，这里设置右键触发取消地图选择并重置地图范围
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void axMap1_MouseDownEvent(object sender, AxMapWinGIS._DMapEvents_MouseDownEvent e)
        {
            //测试在pan模式下，左键单击无法触发鼠标按下事件，右键能够触发，按键信息为2。
            //在selection模式下，左键单击可以触发此事件，按键信息为1.
            //MessageBox.Show(string.Format("按键信息：{0}", e.button));
            //鼠标右键取消地图选择
            if (e.button == 2)
            {
                SelectionExtent = null;
                foreach(var item in MapList)
                {
                    item.shape.SelectNone();
                }
                AxMapWinGIS.AxMap map = sender as AxMapWinGIS.AxMap;
                if (map != null)
                {
                    map.ZoomToMaxExtents();
                }
            }

        }

        private void toolStripBtnPan_Click(object sender, EventArgs e)
        {
            aMap.CursorMode = tkCursorMode.cmPan;
        }

        private void toolStripBtnZoomIn_Click(object sender, EventArgs e)
        {
            aMap.CursorMode = tkCursorMode.cmZoomIn;
        }

        private void toolStripBtnZoomOut_Click(object sender, EventArgs e)
        {

            aMap.CursorMode = tkCursorMode.cmZoomOut;
        }

        private void toolStripBtnSelection_Click(object sender, EventArgs e)
        {
            aMap.CursorMode = tkCursorMode.cmSelection;
        }

      /// <summary>
      /// 选择区域完成事件
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
       private void AxMap1SelectBoxFinal(object sender,AxMapWinGIS._DMapEvents_SelectBoxFinalEvent e)
        {
            // 获取顶层地图的shapefile            
            int Top = aMap.NumLayers-1;
            int handle = aMap.get_LayerHandle(Top);
            Shapefile sf = aMap.get_Shapefile(handle);
            if (sf != null)
            {
                //获取事件参数中的选择范围，左上角右下角的坐标位置
                double left = 0.0;
                double top = 0.0;
                double bottom = 0.0;
                double right = 0.0;
                aMap.PixelToProj(e.left, e.top, ref left, ref top);
                aMap.PixelToProj(e.right, e.bottom, ref right, ref bottom);
                
                //通过左上角右下角的坐标创建一个范围对象
                SelectionExtent = new Extents();
                SelectionExtent.SetBounds(left, bottom, 0.0, right, top, 0.0);

                ShapeSelect(handle, SelectionExtent,aMap);
                
            }
        }

        /// <summary>
        /// 选择图形函数
        /// </summary>
        /// <param name="Layer"></param>
        /// <param name="Ext"></param>
        private void ShapeSelect(int LayerHandle,Extents Ext,AxMapWinGIS.AxMap Map)
        {
            //获取该范围内选中的图形索引数组，遍历数组，设置图形状态为选中（选中状态有两种：INTERSECTION 交会，INCLUSION 包括）
            Shapefile Layer = Map.get_Shapefile(LayerHandle);        
            if (Layer != null && Ext != null)
            {
                Layer.SelectNone();
                object result = null;
                if (Layer.SelectShapes(SelectionExtent, 0.0, SelectMode.INTERSECTION, ref result))
                {
                    int[] shapes = result as int[];
                    if (shapes == null) return;
                    for (int i = 0; i < shapes.Length; i++)
                    {
                        Layer.set_ShapeSelected(shapes[i], true);
                    }
                }
                //放大到选择区域
                Map.ZoomToSelected(LayerHandle);                
                Map.Redraw();
            }
        }
    }
}
